// 10.3 定制操作
/**
 * 很多算法都会比较输入序列中的元素。默认情况下，这类算法使用元素类型的<或==运算符完成比较。
 * 标准库还为这些算法定义了额外的版本，允许我们提供自己定义的操作来代替默认运算符。
 * 例如，sort算法默认使用元素类型的<运算符。但可能我们希望的排序顺序与<所定义的顺序不同，
 * 或是我们的序列可能保存的是未定义<运算符的元素类型（如Sales_data）。在这两种情况下，都需要重载sort的默认行为。
 */

#include <iterator>
#include <vector>
#include <list>
#include <deque>
#include <forward_list>
#include <string>
#include <array>
#include <stack>
#include <queue>
#include <algorithm>
#include <numeric>
using std::swap;
using std::vector, std::list, std::deque, std::forward_list, std::string, std::array, std::stack, std::queue;
#include "../Chapter07/Sales_data.h"
#include <iostream>
using std::begin, std::cbegin, std::end, std::cend, std::find, std::accumulate, std::equal, std::fill, std::fill_n, std::back_inserter;
using std::cin, std::cout, std::endl;
using std::copy, std::replace, std::replace_copy, std::sort, std::unique, std::stable_sort, std::find_if, std::for_each;

void elimDups(vector<string> &words);
bool isShorter(const string &s1, const string &s2);
void biggies(vector<string> &words, vector<string>::size_type sz);
string make_plural(std::ptrdiff_t count, string a, string b);

int main()
{
    // 10.3.2 lambda表达式
    // 根据算法接受一元谓词还是二元谓词，我们传递给算法的谓词必须严格接受一个或两个参数。
    // 但是，有时我们希望进行的操作需要更多参数，超出了算法对谓词的限制。

    // 介绍lambda
    // 到目前为止，我们了解到的可调用对象有四种：函数、函数指针、重载了函数调用运算符的类、以及lambda表达式。
    // 一个lambda表达式表示一个可调用的代码单元。我们可以将其理解为一个未命名的内联函数。
    // 与任何函数类似，一个lambda具有一个返回类型、一个参数列表和一个函数体。
    // 但与函数不同，lambda可能定义在函数内部。一个lambda表达式具有如下形式[插图]
    // [capture list] (parameter list) -> return type { function body }
    // capture list（捕获列表）是一个lambda所在函数中定义的局部变量的列表（通常为空）；
    // 与普通函数不同，lambda必须使用尾置返回（参见6.3.3节，第206页）来指定返回类型。
    // 我们可以忽略参数列表和返回类型，但必须永远包含捕获列表和函数体[插图]
    auto f = []
    { return 42; }; // 一个lambda表达式，我们定义了一个可调用对象f，它不接受参数，返回42。
    cout << f() << endl;

    // 向lambda传递参数
    // 与普通函数不同，lambda不能有默认参数（参见6.5.1节，第211页）
    auto shorter = [](const string &s1, const string &s2)
    { return s1.size() < s2.size(); }; // 一个与isShorter函数完成相同功能的lambda
    // 空捕获列表表明此lambda不使用它所在函数中的任何局部变量。
    vector<string> words{"1", "12", "123", "7", "78", "789"};
    stable_sort(words.begin(), words.end(), shorter); // 当stable_sort需要比较两个元素时，它就会调用给定的这个lambda表达式。

    // 使用捕获列表
    // 虽然一个lambda可以出现在一个函数中，使用其局部变量，但它只能使用那些明确指明的变量。
    // 一个lambda通过将局部变量包含在其捕获列表中来指出将会使用这些变量。捕获列表指引lambda在其内部包含访问局部变量所需的信息。

    // 调用find_if
    biggies(words, 3);

    return 0;
}

void elimDups(vector<string> &words)
{
    // 按字典排序words，以便查找重复单词
    sort(words.begin(), words.end());
    // unique重排输入范围，使得每个单词只出现一次
    // 排列在范围的前部，返回指向不重复区域之后一个位置的迭代器
    auto end_unique = unique(words.begin(), words.end());
    // 使用向量操作erase删除重复单词
    words.erase(end_unique, words.end());
}

// 比较函数，用来按长度排序单词
bool isShorter(const string &s1, const string &s2)
{
    return s1.size() < s2.size();
}

// 求大于等于一个给定长度的单词有多少。我们还会修改输出，使程序只打印大于等于给定长度的单词。
void biggies(vector<string> &words, vector<string>::size_type sz)
{
    elimDups(words); // 将words按字典排序并删除重复单词
    // 按长度排序，长度相同的单词维持字典序
    stable_sort(words.begin(), words.end(), isShorter);
    // 获取一个迭代器，指向第一个满足size() > sz的元素
    auto wc = find_if(words.begin(), words.end(), [sz](const string &a)
                      { return a.size() >= sz; });
    // 计算满足size() > sz的元素的数目
    auto count = words.end() - wc;
    cout << count << " " << make_plural(count, "word", "s") << " of length " << sz << " or longer" << endl;
    // 打印长度大于等于给定值的单词，每个单词后面接一个空格
    for_each(wc, words.end(), [](const string &s)
             { cout << s << " "; });
    cout << endl;
}

string make_plural(std::ptrdiff_t count, string a, string b)
{
    if (count > 1)
        return a + b;
    else
        return a;
}